#include "AlgoHandler.h"
#include <vector>
#include <json/json.h>
#include "passport_api.h"
#include "execmd.h"
#include "work_object.h"
#include "media/MvMedia.h"
#include "config/Config.h"
#include "WebSocketModule/WebSocketHandler.h"

class AlgoHandler::AlgoHandlerImpl {
public:
    AlgoHandlerImpl();
    ~AlgoHandlerImpl();
    int init();
    int deInit();
    int syncImgData(uint8_t* data);
    void processImgData();
    void request();
    void startReg();
    void stopReg();
    void releaseMvMedia();
private:
    std::atomic_bool isInited;
    std::atomic_bool isExit;
    std::atomic_bool recvReq;
    int reqCount;
    struct _thread_object object;
    mv_alg_image_t in_img;
    mv_alg_handle_t handle;
    std::vector<std::string> directionNames = {"脊线朝镜头", "翻页侧朝镜头"};
    std::vector<std::string> coverNames = {"正面朝上", "背面朝上", "夹子上无护照"};
    MVMedia* media;
    bool enablePassportSideReg;
    bool enablePassportCoverReg;
};

AlgoHandler::AlgoHandlerImpl::AlgoHandlerImpl():in_img(), media(nullptr) {
    isInited.store(false);
}

AlgoHandler::AlgoHandlerImpl::~AlgoHandlerImpl() {

}

int AlgoHandler::AlgoHandlerImpl::init() {
    if(isInited.load()) return -1;
    isExit.store(false);
    recvReq.store(false);
    reqCount = 0;

    in_img.width = Config::getInstance()->OutWidth;
    in_img.height = Config::getInstance()->OutHeight;

    in_img.size = in_img.width * in_img.height * 3;
    in_img.pixel_format = MV_ALG_PIXEL_FORMAT_RGB888;
    in_img.data = static_cast<uint8_t*>(malloc(in_img.size));
    if(nullptr == in_img.data) {
        fprintf(stderr, "malloc fail. retry");
        return -1;
    }

    enablePassportCoverReg = Config::getInstance()->enablePassportCoverReg;
    enablePassportSideReg = Config::getInstance()->enablePassportSideReg;

    // 1. 创建 handle, 初始化
    mv_alg_ret_t ret = MV_ALG_RET_SUCCESS;
    handle = mv_alg_create_handle_passport();
    mv_alg_set_config(handle, "Common.device_id", "0");
    ret = mv_alg_init_passport(handle, Config::getInstance()->algoModelPath.c_str());
    if (ret != MV_ALG_RET_SUCCESS)
    {
        fprintf(stderr, "mv_alg_init_passport failed, ret = %d\n", ret);
        return -1;
    }

    // 3. 获取版本号
    mv_alg_version_t version = {};
    ret = mv_alg_get_version_passport(handle, &version);
    if (ret != MV_ALG_RET_SUCCESS)
    {
        fprintf(stderr, "mv_alg_get_version_passport failed, ret = %d\n", ret);
        return -1;
    }
    fprintf(stderr, "version: %s\n", version.version);
    
    media = new MVMedia;
    int rc = media->init(Config::getInstance()->InWidth, Config::getInstance()->InHeight, in_img.width, in_img.height, Config::getInstance()->Rotation);
    if(rc < 0) {
        delete media;
        mv_alg_release_image(&in_img);
        mv_alg_destroy_passport(handle);
        return rc;
    }
    media->registerDataSyncFunc(std::bind(&AlgoHandler::AlgoHandlerImpl::syncImgData, this, std::placeholders::_1));
    object.work_flag.store(false);
    object.work_thread = std::move(std::thread(&AlgoHandler::AlgoHandlerImpl::processImgData, this));
    isInited.store(true);
    return 0;
}

int AlgoHandler::AlgoHandlerImpl::deInit() {
    if(!isInited.load()) {
        fprintf(stderr, "no need to call %s\n", __func__);
        return 0;
    }
    isExit.store(true);
    if(object.work_thread.joinable()) {
        object.work_cond.notify_all();
        object.work_thread.join();
    }
    if(media) {
        delete media;
        media = nullptr;
    }

    mv_alg_ret_t ret = MV_ALG_RET_SUCCESS;
    // 5. 释放图片内存
    ret = mv_alg_release_image(&in_img);
    // 6. 反初始化, 销毁 handle
    ret = mv_alg_destroy_passport(handle);
    isInited.store(false);
    return 0;
}

int AlgoHandler::AlgoHandlerImpl::syncImgData(uint8_t* data) {
    if(!isInited.load()) return -1;
    if(!object.work_flag.load()) {
        memcpy(in_img.data, data, in_img.size);
        object.work_flag.store(true);
        object.work_cond.notify_one();
    }
    return 0;
}

void AlgoHandler::AlgoHandlerImpl::processImgData() {
    while(!isExit.load()) {
        std::unique_lock<std::mutex> lk(object.work_lock);
        object.work_cond.wait(lk, [this](){return isExit.load() || object.work_flag.load();});
        if(isExit.load()) break;
        mv_alg_ret_t ret = MV_ALG_RET_SUCCESS;
#if 0
        static int index = 0;
        std::string path = "/userdata/test/" + std::to_string(index) + ".jpg";
        mv_alg_save_image(&in_img, path.c_str());
        index++;
#endif
        if(enablePassportSideReg) {
            // 4. 护照脊线/翻页侧识别
            mv_alg_passport_side_t side;
            auto start = std::chrono::high_resolution_clock::now();
            ret = mv_alg_passport_side(handle, &in_img, &side);
            auto end = std::chrono::high_resolution_clock::now();
            auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
            fprintf(stderr, "time: %d ms\n", (int)duration);
            if (ret != MV_ALG_RET_SUCCESS) {
                fprintf(stderr, "mv_alg_passport failed, ret = %d\n", ret);
            }
            std::string direction_name = directionNames[(int)side];
            fprintf(stderr, "direction: %s\n", direction_name.c_str());
            if(recvReq.load()) {
                // TODO send WebSocket Message
                Json::Value value;
                value["cmdNo"] = 50;
                Json::Value jsonData;
                jsonData["result"] = side;
                value["data"] = jsonData;
                Json::FastWriter fw;
                std::string valueString = fw.write(value);
                websocketSendMsg(valueString);
                recvReq.store(false);
            }
        }
        if(enablePassportCoverReg) {
            // 5. 护照封面朝向
            mv_alg_passport_cover_t cover;
            auto start = std::chrono::high_resolution_clock::now();
            ret = mv_alg_passport_cover(handle, &in_img, &cover);
            auto end = std::chrono::high_resolution_clock::now();
            auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
            fprintf(stderr, "time: %d ms\n", (int)duration);
            if (ret != MV_ALG_RET_SUCCESS)
            {
                fprintf(stderr, "mv_alg_passport failed, ret = %d\n", ret);
            }
            std::string cover_name = coverNames[(int)cover];
            fprintf(stderr, "cover: %s\n", cover_name.c_str());
            if(recvReq.load()) {
                // TODO send WebSocket Message
                Json::Value value;
                value["cmdNo"] = 51;
                Json::Value jsonData;
                jsonData["result"] = cover;
                value["data"] = jsonData;
                Json::FastWriter fw;
                std::string valueString = fw.write(value);
                websocketSendMsg(valueString);
                recvReq.store(false);
            }
        }
        object.work_flag.store(false);
    }
}

void AlgoHandler::AlgoHandlerImpl::request() {
    if(!isInited.load()) return;
    recvReq.store(true);
}

void AlgoHandler::AlgoHandlerImpl::startReg() {
    if(!isInited.load()) return;
    recvReq.store(false);
    media->startReg();
}

void AlgoHandler::AlgoHandlerImpl::stopReg() {
    if(!isInited.load()) return;
    recvReq.store(false);
    media->stopReg();
}

void AlgoHandler::AlgoHandlerImpl::releaseMvMedia() {
    if(media)
        media->release();
}

AlgoHandler::AlgoHandler():pImpl(std::make_unique<AlgoHandlerImpl>()) {

}

AlgoHandler::~AlgoHandler() {
    deInit();
}

int AlgoHandler::init() {
    return pImpl->init();
}

int AlgoHandler::deInit() {
    return pImpl->deInit();
}

void AlgoHandler::request() {
    pImpl->request();
}

void AlgoHandler::startReg() {
    pImpl->startReg();
}

void AlgoHandler::stopReg() {
    pImpl->stopReg();
}

AlgoHandler* AlgoHandler::getInstance() {
    static AlgoHandler handler;
    return &handler;
}

void AlgoHandler::releaseMvMedia() {
    pImpl->releaseMvMedia();
}